window._$ = function() {
  return new $(arguments);
};


(function(){

  // From jQuery
  var strExpr = /^[^<]*(<(.|\s)+>)[^>]*$|^#([\w-]+)$/;
  var selExpr = /^.[^:#\[\.,]*$/;
  var sepExpr = /\s+/;
  
  
  var Geneva = Class.create( Enumerable, {
    // mixin Enumerable so our methods support collections of elements
    initialize: function ( jselect, jctext ) {

      if ( typeof jselect === 'string' ) {

        var match = strExpr.exec( jselect );

        if ( match )
          this.elements = [ $( match[3] ) ];

        else {
          if ( match = selExpr.exec( jselect ) ) {

            this.elements = $$( match );

            if ( this.elements.length === 0 ) {
              this.elements = Try.these(
                  function() { 
                    return [ $( match[0] ) ]; 
                  }
              ) || null;
            } 
          } else
              this.elements = $$( jselect );
        }
      }
      this.toArray();
    }
  });
  

  var ignore  = [ 'Simulated', 'ByTag', 'classNames', 'getElementsByClassName', 
                  'getElementsBySelector', 'immediateDescendants' ];


  function extendWith ( source ) {
    for ( var method in source ) {

      //console.log(method);

      //ignore depracated
      if (/^Simulated|ByTag|classNames|getElementsByClassName|getElementsBySelector|immediateDescendants$/.test(method)) continue;

      // copy source obj methods to Geneva

      Geneva.prototype[method] = 
        (function ( method ) {
          return function() {
            var args = $A(arguments), _first = false, _result, _elements = [];

            //console.log(method);
            //console.log(arguments);


            this.elements.each(function(element, i) {

              _result = source[method]
                          .apply( null, 
                                  args.length 
                                  ? [element].concat(args) 
                                  : [element] );

              if ( Object.isUndefined(_result) || 
                    !Object.isElement(_result) ) {

                      _first = true;
                      throw $break;
              }

              _result && (_elements[i] = element);
            });

            if (_first) return _result;

            this.elements = _elements;
            return this;
          }
        })( method );
    };
  }

  extendWith(Element.Methods);
  extendWith(Form.Element.Methods);

  Geneva.addMethods({
    inspect: function(){
      return Object.inspect(this.elements);
    },
    hover: function(over, out) {
      return this.observe('mouseover', function(e) {
        over.call(e.target, e);
      }).observe('mouseout', function(e) {
        out.call(e.target, e);
      });
    },

    _each: function(iterator) {
      this.elements._each(iterator);
    },
    first: function() {
      return this.elements[0];
    },
    last: function() {
      return this.elements.last();
    },
    toArray: function() {
      Array.prototype.push.apply(this, this.elements);
    }
  });

  $w("blur focus load resize scroll unload click dblclick " +
      "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave " +
      "change select submit keydown keypress keyup error").each(function(type) {

      Geneva.prototype[type] = (  function(type) {
          return function(fn) {
            return this.observe(type, function ( evt ) {
                fn.call(Event.element(evt), evt);
            });
          }
      })(type);
  });


  this.$_ = function ( jselect ) {
    return new Geneva ( jselect );
  };

  this.$_.addMethods = function(methods) {

    console.log(methods);

    for (var name in methods) {
      Wrapper.prototype[name] = methods[name];
    }
  };

  window.$_ = function ( jselect ) {
    return new Geneva ( jselect );
  };


  // Give prototype's methods back with adjustments for $_
  Element.addMethods = (function(source) {
    var fn = function() {
      // apply method source and arguments to 
      source.apply( source, arguments );

      // repeat method extenstion
      extendWith(Element.Methods);
      extendWith(Form.Element.Methods);
    };

    // bind the source to the method and store as string
    fn.toString = source.toString.bind( source );

    // return fn to Element
    return fn;
  })(Element.addMethods);

  [ Form, Form.Methods, Form.Element.Methods ]
    .each( function ( dest ) {
    Object.extend( dest, Geneva );
  });  

  Element.addMethods();
})();




